import numpy as np
from fastdtw import fastdtw
from scipy.spatial.distance import euclidean
import json
from audio_repository import StandardAudioRepository

class ScoreAnalyzer:
    """声调评分分析器，使用DTW和欧氏距离算法"""
    
    @staticmethod
    def extract_pitch_sequence(audio_data):
        """从音频数据中提取基频序列
        
        Args:
            audio_data (dict): 包含频率和时间数据的字典
            
        Returns:
            list: 基频序列（频率值列表）
        """
        try:
            if isinstance(audio_data, str):
                data = json.loads(audio_data)
            else:
                data = audio_data
                
            if not isinstance(data, dict) or "data" not in data:
                return None
                
            # 提取频率数据并过滤有效值
            frequencies = []
            for point in data["data"]:
                freq = float(point.get("frequency", 0))
                if freq > 0:  # 只保留有效的频率值
                    frequencies.append(freq)
                    
            return frequencies if frequencies else None
            
        except Exception as e:
            print(f"提取基频序列错误: {e}")
            return None
    
    @staticmethod
    def normalize_sequences(seq1, seq2, target_length=50):
        """将两个序列归一化到相同长度
        
        Args:
            seq1 (list): 第一个序列
            seq2 (list): 第二个序列
            target_length (int): 目标长度
            
        Returns:
            tuple: (归一化后的序列1, 归一化后的序列2)
        """
        if not seq1 or not seq2:
            return None, None
            
        # 使用线性插值归一化到相同长度
        def interpolate_sequence(seq, target_len):
            if len(seq) == 1:
                return [seq[0]] * target_len
                
            # 创建原始索引
            original_indices = np.linspace(0, len(seq) - 1, len(seq))
            # 创建目标索引
            target_indices = np.linspace(0, len(seq) - 1, target_len)
            # 线性插值
            interpolated = np.interp(target_indices, original_indices, seq)
            return interpolated.tolist()
        
        norm_seq1 = interpolate_sequence(seq1, target_length)
        norm_seq2 = interpolate_sequence(seq2, target_length)
        
        return norm_seq1, norm_seq2
    
    @staticmethod
    def calculate_euclidean_score(user_seq, std_seq, max_distance=200):
        """使用欧氏距离计算评分
        
        Args:
            user_seq (list): 用户基频序列
            std_seq (list): 标准基频序列
            max_distance (float): 最大距离阈值，用于归一化分数
            
        Returns:
            dict: 包含分数和详细信息的字典
        """
        try:
            if not user_seq or not std_seq:
                return {
                    "score": 0,
                    "distance": float('inf'),
                    "level": "极差",
                    "error": "序列为空"
                }
            
            # 归一化序列到相同长度
            norm_user, norm_std = ScoreAnalyzer.normalize_sequences(user_seq, std_seq)
            
            if not norm_user or not norm_std:
                return {
                    "score": 0,
                    "distance": float('inf'),
                    "level": "极差",
                    "error": "序列归一化失败"
                }
            
            # 计算欧氏距离
            distance = euclidean(norm_user, norm_std)
            
            # 计算分数（距离越小，分数越高）
            score = max(0, 100 * (1 - distance / max_distance))
            score = round(score, 2)
            
            # 确定等级
            level = ScoreAnalyzer.get_score_level(score)
            
            return {
                "score": score,
                "distance": round(distance, 4),
                "level": level,
                "algorithm": "欧氏距离",
                "normalized_length": len(norm_user)
            }
            
        except Exception as e:
            return {
                "score": 0,
                "distance": float('inf'),
                "level": "极差",
                "error": str(e)
            }
    
    @staticmethod
    def calculate_dtw_score(user_seq, std_seq, max_distance=300):
        """使用DTW算法计算评分
        
        Args:
            user_seq (list): 用户基频序列
            std_seq (list): 标准基频序列
            max_distance (float): 最大距离阈值，用于归一化分数
            
        Returns:
            dict: 包含分数和详细信息的字典
        """
        try:
            if not user_seq or not std_seq:
                return {
                    "score": 0,
                    "distance": float('inf'),
                    "level": "极差",
                    "error": "序列为空"
                }
            
            # 转换为numpy数组
            user_array = np.array(user_seq)
            std_array = np.array(std_seq)
            
            # 计算DTW距离
            distance, path = fastdtw(user_array, std_array, dist=euclidean)
            
            # 计算分数（距离越小，分数越高）
            score = max(0, 100 * (1 - distance / max_distance))
            score = round(score, 2)
            
            # 确定等级
            level = ScoreAnalyzer.get_score_level(score)
            
            return {
                "score": score,
                "distance": round(distance, 4),
                "level": level,
                "algorithm": "DTW",
                "path_length": len(path)
            }
            
        except Exception as e:
            return {
                "score": 0,
                "distance": float('inf'),
                "level": "极差",
                "error": str(e)
            }
    
    @staticmethod
    def get_score_level(score):
        """根据分数确定等级
        
        Args:
            score (float): 分数 (0-100)
            
        Returns:
            str: 等级描述
        """
        if score >= 80:
            return "优秀"
        elif score >= 60:
            return "合格"
        elif score >= 26:
            return "较差"
        else:
            return "极差"
    
    @staticmethod
    def analyze_tone_score(user_audio_data, character, pinyin, tone_type=None):
        """完整的声调评分分析
        
        Args:
            user_audio_data (dict): 用户音频数据
            character (str): 汉字
            pinyin (str): 拼音
            tone_type (int, optional): 声调类型
            
        Returns:
            dict: 包含两种算法评分结果的字典
        """
        try:
            # 提取用户基频序列
            user_seq = ScoreAnalyzer.extract_pitch_sequence(user_audio_data)
            if not user_seq:
                return {
                    "status": "error",
                    "message": "无法提取用户音频的基频序列"
                }
            
            # 获取标准音频数据
            standard_audio = StandardAudioRepository.get_by_character_and_pinyin(character, pinyin)
            if not standard_audio:
                return {
                    "status": "error",
                    "message": f"未找到汉字'{character}'拼音'{pinyin}'的标准音频数据"
                }
            
            # 解析标准频率数据
            if not standard_audio.get('frequency_data'):
                return {
                    "status": "error",
                    "message": "标准音频数据中没有频率信息"
                }
            
            try:
                std_freq_data = json.loads(standard_audio['frequency_data'])
                std_seq = ScoreAnalyzer.extract_pitch_sequence(std_freq_data)
            except Exception as e:
                return {
                    "status": "error",
                    "message": f"解析标准音频频率数据失败: {e}"
                }
            
            if not std_seq:
                return {
                    "status": "error",
                    "message": "无法提取标准音频的基频序列"
                }
            
            # 计算两种算法的评分
            euclidean_result = ScoreAnalyzer.calculate_euclidean_score(user_seq, std_seq)
            dtw_result = ScoreAnalyzer.calculate_dtw_score(user_seq, std_seq)
            
            # 计算综合评分（两种算法的加权平均）
            euclidean_weight = 0.4
            dtw_weight = 0.6  # DTW权重更高，因为它更适合时间序列
            
            if euclidean_result.get("error") or dtw_result.get("error"):
                # 如果某个算法出错，使用另一个算法的结果
                if not euclidean_result.get("error"):
                    final_score = euclidean_result["score"]
                    final_level = euclidean_result["level"]
                elif not dtw_result.get("error"):
                    final_score = dtw_result["score"]
                    final_level = dtw_result["level"]
                else:
                    final_score = 0
                    final_level = "极差"
            else:
                final_score = euclidean_result["score"] * euclidean_weight + dtw_result["score"] * dtw_weight
                final_score = round(final_score, 2)
                final_level = ScoreAnalyzer.get_score_level(final_score)
            
            return {
                "status": "success",
                "character": character,
                "pinyin": pinyin,
                "tone_type": standard_audio.get('tone_type'),
                "final_score": final_score,
                "final_level": final_level,
                "euclidean_score": euclidean_result,
                "dtw_score": dtw_result,
                "user_sequence_length": len(user_seq),
                "standard_sequence_length": len(std_seq)
            }
            
        except Exception as e:
            return {
                "status": "error",
                "message": f"评分分析失败: {e}"
            } 